package com.sprouting.personal.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.hutool.json.JSONUtil;
import com.sprouting.personal.annotations.ApiIdempotent;
import com.sprouting.personal.handler.NonStaticResourceHttpRequestHandler;
import com.sprouting.personal.interfaces.common.Constant;
import com.sprouting.personal.model.common.ResultJson;
import com.sprouting.personal.model.common.jsr.AddGroup;
import com.sprouting.personal.model.common.jsr.UpdGroup;
import com.sprouting.personal.model.entity.Config;
import com.sprouting.personal.model.vo.VideoResource;
import com.sprouting.personal.service.ConfigService;
import com.sprouting.personal.utils.CheckTool;
import com.sprouting.personal.utils.IdTool;
import com.sprouting.personal.utils.NetTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 视频播放
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2022/11/17 11:04
 */
@RequestMapping("/video")
@RestController
@Slf4j
public class VideoController {

    @Resource
    private NonStaticResourceHttpRequestHandler nonStaticResourceHttpRequestHandler;
    @Autowired
    private ConfigService configService;

    /**
     * 读取的视频类型
     */
    @Value("#{'${video.resources.support.suffix}'.split(',')}")
    private List<String> videoSuffixList;
    @Value("${server.port}")
    private String port;
    @Value("${server.servlet.context-path:''}")
    private String contextPath;
    @Value("${sa-token.token-name}")
    private String tokenKey;


    /**
     * 读取视频流，这种方式不需要等加载，可以实现拉动播放等效果
     * @param request
     * @param response
     * @param path 要播放的视频文件的地址， d://xxx/xx.mp4 格式
     */
    @GetMapping("/getVideoStream")
    public void getVideoStream(HttpServletRequest request, HttpServletResponse response, String path){
        if (CheckTool.checkNull(path)){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            log.error("未传递有效视频播放地址");
            return;
        }
        File file = new File(path);
        if (!file.exists()){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            log.error("视频资源映射目录文件路径错误或不存在");
            return;
        }
        if (!file.isFile()){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            log.error("不是一个文件");
            return;
        }
        log.info("开始视频读取数据......");

        request.setAttribute(NonStaticResourceHttpRequestHandler.ATTR_FILE, path);
        try {
            nonStaticResourceHttpRequestHandler.handleRequest(request, response);
        } catch (ServletException | IOException e) {
            if (e instanceof ClientAbortException){
                log.info("源端关闭了视频流");
            } else {
                log.error("视频获取异常:", e);
            }
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        }
    }

    /**
     * 获取服务器播放地址-用于视频播放
     * @return
     */
    @GetMapping("/getServerAddress")
    public ResultJson getServerAddress() {
        // 获取本地电脑ip地址
        String ip = NetTool.getLocalHostLANAddress();
        // 返回的是后台的视频流读取地址
        String server = "http://" + ip + ":" + port + Constant.Base.SYS_PREFIX + "/video/getVideoStream?" + tokenKey + "=" + StpUtil.getTokenValue() + "&path=";
        return ResultJson.success(server);
    }

    /**
     * 删除指定的播放视频
     * @param path 视频路径
     * @return
     */
    @PostMapping("/delPlayVideo")
    public ResultJson delPlayVideo(String path){
        if (CheckTool.checkNull(path)){
            return ResultJson.errorIllegal();
        }
        Config config = configService.getConfigByCode(Constant.ConfigCode.VIDEO_MAPPING_RESOURCE, Constant.DefaultValue.STRING);
        if (!path.contains(config.getValue())){
            // 防止删除其他的路径，必须是视频播放地址的路径
            return ResultJson.error("非法路径");
        }

        File file = new File(path);
        if (!file.exists()){
            return ResultJson.error("视频资源映射目录文件夹路径错误或不存在");
        }
        if (file.isDirectory()){
            return ResultJson.error("视频资源映射目录不能是一个目录");
        }

        FileUtil.del(file);
        return ResultJson.success();
    }


    /**
     * 将当前页面的url获取到请求地址，生成二维码地址
     * @param dev 开发还是生产 dev 开发
     * @return
     */
    @PostMapping("/getPageUrl")
    public ResultJson getPageUrl(String dev) {
        if (CheckTool.checkNull(dev)){
            dev = "dev";
        }

        String ip = NetTool.getLocalHostLANAddress();
        String server;
        if ("dev".equals(dev)){
            // 开发，如果还不能访问，需要考虑是否前端 设置的是 localhost，要修改为 0.0.0.0 或者 当前IP否则无法远程访问，如果发现访问进入页面但是报错，那则是前端没有设置正确IP的问题
            server = "http://" + ip + ":8083" + "/#/toVideoPlaybackNoBorder";

        } else {
            // 生产
            server = "http://" + ip + ":" + port + "/index.html#/toVideoPlaybackNoBorder";
        }

        log.info(server);
        // 二维码
        QrConfig config = new QrConfig(250, 250);
        // 设置边距，既二维码和背景之间的边距
        config.setMargin(3);
        String base64 = QrCodeUtil.generateAsBase64(server, config, ImgUtil.IMAGE_TYPE_PNG);
        return ResultJson.success(base64);
    }

    /**
     * 获取本机的IP地址
     * @return
     */
    @GetMapping("/getIp")
    public ResultJson getIp(){
        String ip = NetTool.getLocalHostLANAddress();
        return ResultJson.success(ip + ":" + port + Constant.Base.SYS_PREFIX);
    }

    /**
     * 读取可查看的视频的资源
     * @return
     */
    @PostMapping("/getResourcesList")
    public ResultJson getResourcesList(){
        // 该配置是默认的
        Config config = configService.getConfigByCode(Constant.ConfigCode.VIDEO_MAPPING_RESOURCE, Constant.DefaultValue.STRING);

        // 检查合法性
        String value = config.getValue();
        if (CheckTool.checkNull(value)){
            return ResultJson.error("未初始化视频资源映射目录，请先前往默认配置中设置目录");
        }
        File file = new File(value);
        if (!file.exists()){
            return ResultJson.error("视频资源映射目录文件夹路径错误或不存在");
        }
        if (!file.isDirectory()){
            return ResultJson.error("视频资源映射目录不能是一个文件");
        }

        // 过滤配置，只读取指定后缀
        FileFilter fileFilter = pathname -> {
            String suffix = FileUtil.getSuffix(pathname);
            if (CheckTool.checkNull(suffix)){
                return false;
            }

            suffix = suffix.toLowerCase();
            if (videoSuffixList.contains(suffix)){
                return true;
            }
            return false;
        };

        List<File> fileList = FileUtil.loopFiles(file, fileFilter);
        List<VideoResource> videoResourceList = new ArrayList<>();

        if (CollUtil.isNotEmpty(fileList)){
            for (File result : fileList){
                VideoResource videoResource = new VideoResource();
                videoResource.setName(FileUtil.getName(result));
                videoResource.setPath(result.getPath());
                // 会转换为各种大小，比如G、mb等
                videoResource.setLength(FileUtil.readableFileSize(result));
                videoResourceList.add(videoResource);
            }
        }

        return ResultJson.success(videoResourceList);
    }

    /**
     * 获取可以播放的直播列表
     * @return
     */
    @PostMapping("/getLiveBroadcastList")
    @ApiIdempotent
    public ResultJson getLiveBroadcastList(){
        Config config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, StpUtil.getLoginIdAsString());
        if (config == null){
            // 构建当前用户自己的阅读地址，直接复制默认的
            config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, Constant.DefaultValue.STRING);
            config.setId(IdTool.getId());
            config.setCode(Constant.ConfigCode.LIVE_BROADCAST);
            config.setUserId(StpUtil.getLoginIdAsString());
            configService.save(config);
        }

        List<VideoResource> videoResourceList = new ArrayList<>();
        String value = config.getValue();
        if (CheckTool.checkNull(value)){
            return ResultJson.success(videoResourceList);
        }
        videoResourceList = JSONUtil.toList(value, VideoResource.class);
        return ResultJson.success(videoResourceList);
    }

    /**
     * 保存一个视频直播源
     * @param videoResource 资源信息
     * @return
     */
    @PostMapping("/saveLiveBroadcast")
    @ApiIdempotent
    public ResultJson saveLiveBroadcast(@Validated(AddGroup.class) VideoResource videoResource){
        Config config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, StpUtil.getLoginIdAsString());
        if (config == null){
            // 构建当前用户自己的阅读地址，直接复制默认的
            config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, Constant.DefaultValue.STRING);
            config.setId(IdTool.getId());
            config.setCode(Constant.ConfigCode.LIVE_BROADCAST);
            config.setUserId(StpUtil.getLoginIdAsString());
            configService.save(config);
        }

        videoResource.setId(IdTool.getId());
        List<VideoResource> videoResourceList = new ArrayList<>();
        String value = config.getValue();
        if (CheckTool.checkNull(value)){

        } else {
            videoResourceList = JSONUtil.toList(value, VideoResource.class);
        }
        videoResourceList.add(videoResource);

        // 入库
        String jsonStr = JSONUtil.toJsonStr(videoResourceList);
        config.setValue(jsonStr);
        configService.updateById(config);

        return ResultJson.success(videoResource);
    }

    /**
     * 更新一个视频直播源
     * @param videoResource 资源信息
     * @return
     */
    @PostMapping("/updLiveBroadcast")
    @ApiIdempotent
    public ResultJson updLiveBroadcast(@Validated(UpdGroup.class) VideoResource videoResource){
        Config config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, StpUtil.getLoginIdAsString());
        if (config == null){
            return ResultJson.errorIllegal();
        }

        String value = config.getValue();
        List<VideoResource> videoResourceList = JSONUtil.toList(value, VideoResource.class);
        for (VideoResource db : videoResourceList){
            if (db.getId().equals(videoResource.getId())){
                db.setPath(videoResource.getPath());
                db.setName(videoResource.getName());
            }
        }

        String jsonStr = JSONUtil.toJsonStr(videoResourceList);
        config.setValue(jsonStr);
        configService.updateById(config);
        return ResultJson.success();
    }

    /**
     * 删除一个直播源
     * @param id 直播源的ID
     * @return
     */
    @PostMapping("/delLiveBroadcastById")
    @ApiIdempotent
    public ResultJson delLiveBroadcastById(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }

        Config config = configService.getConfigByCode(Constant.ConfigCode.LIVE_BROADCAST, StpUtil.getLoginIdAsString());
        if (config == null){
            return ResultJson.errorIllegal();
        }

        String value = config.getValue();
        List<VideoResource> videoResourceList = new ArrayList<>();
        if (!CheckTool.checkNull(value)){
            videoResourceList = JSONUtil.toList(value, VideoResource.class);
        }

        List<VideoResource> collect = videoResourceList.stream().filter(obj -> !obj.getId().equals(id)).collect(Collectors.toList());
        String jsonStr = JSONUtil.toJsonStr(collect);
        config.setValue(jsonStr);
        configService.updateById(config);
        return ResultJson.success();
    }

    /**
     * 生成二维码图片
     * @param url 要生成的东西
     * @return 返回base64的图片
     */
    @PostMapping("/getRr")
    public ResultJson getRr(String url){
        if (CheckTool.checkNull(url)){
            return ResultJson.errorIllegal();
        }
        QrConfig config = new QrConfig(300, 300);
        // 设置边距，既二维码和背景之间的边距
        config.setMargin(3);
        String base64 = QrCodeUtil.generateAsBase64(url, config, ImgUtil.IMAGE_TYPE_PNG);
        return ResultJson.success(base64);
    }

}
